-- AutoThresholds.lua (reworked dynamic caps + faster convergence + crit-driven Ultra)
local ADDON, DFFT = ...
if not DFFT or not DFFT.DB then return end
local DB = DFFT.DB

-- ===============================
-- State
-- ===============================
DFFT.Auto = DFFT.Auto or {}
local Auto = DFFT.Auto

Auto.enabled        = (Auto.enabled ~= false)
Auto.role           = Auto.role or "DAMAGER"
Auto.ilvl           = Auto.ilvl or 0
Auto.recent_all     = Auto.recent_all or {}
Auto.recent_crit    = Auto.recent_crit or {}
Auto.recent_crit_tg = Auto.recent_crit_tg or {}
Auto.maxSamples     = Auto.maxSamples or 240
Auto.lastRecalc     = Auto.lastRecalc or 0
Auto.dirty          = (Auto.dirty ~= false)

-- ===============================
-- Utils
-- ===============================
local function clamp(x, a, b) if x < a then return a elseif x > b then return b else return x end end

local function copySorted(t)
  local c = {}
  for i=1,#t do c[i] = t[i] end
  table.sort(c)
  return c
end

local function percentile(t, p)
  if not t or #t == 0 then return 0 end
  local a = copySorted(t)
  local n = #a
  local k = clamp(math.floor((p/100) * n + 0.5), 1, n)
  return a[k]
end

local function dynamicCap(ilvl)
  ilvl = tonumber(ilvl) or 0
  -- piecewise: <550 -> ~2.4 ; 550..650 -> up to 3.0 ; >650 -> up to 3.6
  if ilvl <= 0 then return 2.6 end
  if ilvl < 550 then
    return 2.4 + (ilvl/550)*0.2      -- 2.4..2.6
  elseif ilvl <= 650 then
    return 2.6 + ((ilvl-550)/100)*0.4 -- 2.6..3.0
  else
    local extra = clamp((ilvl-650)/200, 0, 1) * 0.6
    return 3.0 + extra               -- 3.0..3.6
  end
end

local function round(x) return (x >= 0) and math.floor(x + 0.5) or math.ceil(x - 0.5) end

local function pushSample(arr, v, maxN)
  arr[#arr+1] = v
  if #arr > maxN then table.remove(arr, 1) end
end

-- ===============================
-- Player context
-- ===============================
function DFFT.Auto_UpdatePlayer()
  local role = "DAMAGER"
  if GetSpecialization and GetSpecializationRole then
    local spec = GetSpecialization()
    if spec then role = (GetSpecializationRole(spec) or role) end
  end
  Auto.role = role

  if GetAverageItemLevel then
    local _, ilvl = GetAverageItemLevel()
    Auto.ilvl = math.max(0, math.floor(ilvl or 0))
  end
end

-- Keep existing thresholds, just make sure we have player context
function DFFT.Auto_InitSeed()
  DFFT.Auto_UpdatePlayer()
  Auto.dirty = true
end

-- ===============================
-- Recalculation from samples
-- ===============================
local function recalcFromSamples()
  local now = GetTime and GetTime() or 0
  Auto.lastRecalc = now

  local N = #Auto.recent_all
  if N < 8 then return end

  local s40 = percentile(Auto.recent_all, 40)
  local b75 = percentile(Auto.recent_all, 75)

  local critSet = (#Auto.recent_crit_tg >= 5) and Auto.recent_crit_tg or Auto.recent_crit
  local c95 = percentile(critSet, 95)

  -- dynamic cap relative to Big
  local cap = dynamicCap(Auto.ilvl)

  local smallMax  = math.max(1, round(s40))
  local bigMin    = math.max(smallMax+1, round(b75))

  -- Ultra: crit-driven with cap floor and spacing guard
  local ultraByCap   = bigMin * cap
  local vbigCandidate= math.max(c95, ultraByCap)

  -- spacing to avoid ridiculous gaps at low levels
  local spacingMin   = math.max( round(3 * smallMax), round(0.40 * bigMin) )
  local veryBigMin   = math.max( vbigCandidate, bigMin + spacingMin )

  -- write DB
  DB.smallMax    = smallMax
  DB.bigMin      = bigMin
  DB.veryBigMin  = veryBigMin
end
DFFT.recalcFromSamples = recalcFromSamples

-- Public trigger for UI/slash
function DFFT.Auto_RecalibrateNow()
  recalcFromSamples()
  if print then
    print(("|cff88ff88DFFT|r recalibré: small ≤ |cffffff00%s|r, big ≥ |cffffff00%s|r, ULTRA ≥ |cffffff00%s|r (ilvl %d, cap×%.2f)"):
      format(DB.smallMax or 0, DB.bigMin or 0, DB.veryBigMin or 0, Auto.ilvl or 0, dynamicCap(Auto.ilvl)))
  end
end

function DFFT.Auto_PrintThresholds()
  if print then
    print(("|cff88ff88DFFT|r thresholds — small ≤ |cffffff00%s|r, big ≥ |cffffff00%s|r, ULTRA ≥ |cffffff00%s|r (role=%s, ilvl=%d, samples=%d, crits=%d)"):
      format(DB.smallMax or 0, DB.bigMin or 0, DB.veryBigMin or 0, Auto.role or "?", Auto.ilvl or 0, #Auto.recent_all, #Auto.recent_crit))
  end
end

-- ===============================
-- Collector
-- ===============================
function DFFT.Auto_OnNewAmount(amount, isCrit, onTarget)
  if not (DB.autoThresholds and Auto.enabled) then return end
  local a = tonumber(amount) or 0
  if a <= 0 then return end

  -- update context periodically
  if not Auto._lastCtx or ((GetTime() or 0) - (Auto._lastCtx or 0) > 10) then
    DFFT.Auto_UpdatePlayer()
    Auto._lastCtx = GetTime() or 0
  end

  pushSample(Auto.recent_all,  a, Auto.maxSamples)
  if isCrit then
    pushSample(Auto.recent_crit, a, Auto.maxSamples)
    if onTarget ~= false then
      pushSample(Auto.recent_crit_tg, a, Auto.maxSamples)
    end
  end

  local now = GetTime and GetTime() or 0
  local N = #Auto.recent_all
  if (N >= 12 and (now - (Auto.lastRecalc or 0)) > 1.0) or (N % 12 == 0) then
    recalcFromSamples()
  end
end
